﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using ProjectHopur23.Repositories;
using System.Threading.Tasks;
using Microsoft.AspNet.SignalR;
using ProjectHopur23.Models;

namespace ProjectHopur23.Controllers
{
	public class TicTacToeHub : GameHub
	{
		#region RepositoryFunctions
		/// <summary>
		/// Get all TicTacToeStates by activeGameId
		/// </summary>
		public IEnumerable<TicTacToeState> GetAllTicTacToeStatesByActiveGameId(int activeGameId)
		{
			IEnumerable<TicTacToeState> theStates = ( from tttState in rep.GetAllTicTacToeStates()
													  where activeGameId == tttState.activeGameId  
													  select tttState);

			return theStates; 
		}

		public IEnumerable<TicTacToeState> GetGameStates(string activeGameId)
		{
			return GetAllTicTacToeStatesByActiveGameId(Convert.ToInt32(activeGameId));
		}
		#endregion

		#region Functions
		/// <summary>
		/// Checks the TicTacToeStates table to see if 
		/// the User in the ActiveGame has won the game
		/// </summary>
		public int CheckForWin(int activeGameId)
		{
			TicTacToeState[] gameState = GetAllTicTacToeStatesByActiveGameId(activeGameId).ToArray();

			// Matrix setup:
			// 0 1 2
			// 3 4 5
			// 6 7 8

/*0 1 2*/	if(((gameState[0].playerId == gameState[1].playerId) && (gameState[1].playerId == gameState[2].playerId) && gameState[0].playerId != -1)
/*3 4 5*/	|| ((gameState[3].playerId == gameState[4].playerId) && (gameState[4].playerId == gameState[5].playerId) && gameState[3].playerId != -1)
/*6 7 8*/	|| ((gameState[6].playerId == gameState[7].playerId) && (gameState[7].playerId == gameState[8].playerId) && gameState[6].playerId != -1)
/*0 3 6*/	|| ((gameState[0].playerId == gameState[3].playerId) && (gameState[3].playerId == gameState[6].playerId) && gameState[0].playerId != -1)
/*1 4 7*/	|| ((gameState[1].playerId == gameState[4].playerId) && (gameState[4].playerId == gameState[7].playerId) && gameState[1].playerId != -1)
/*2 5 8*/	|| ((gameState[2].playerId == gameState[5].playerId) && (gameState[5].playerId == gameState[8].playerId) && gameState[2].playerId != -1)
/*0 4 8*/	|| ((gameState[0].playerId == gameState[4].playerId) && (gameState[4].playerId == gameState[8].playerId) && gameState[0].playerId != -1)
/*2 4 6*/	|| ((gameState[2].playerId == gameState[4].playerId) && (gameState[4].playerId == gameState[6].playerId) && gameState[2].playerId != -1))
			{
				//Victory
				return 1;
			}

			for(int i = 0; i < 9; i++)
			{
				if(gameState[i].playerId == -1)
				{
					//The game is not over yet
					return 0;
				}
			}
			
			//The game is over and it is a tie
			return -1;
		}

		/// <summary>
		/// Checks the TicTacToeStates to see if
		/// the cell is empty
		/// </summary>
		public bool CellIsEmpty(TicTacToeState theState)
		{
			if(theState.playerId == -1)
			{
				return true;
			}
			else
			{
				return false;
			}
		}

		/// <summary>
		/// Players have Id's 0 or 1. Use this function switch active player.
		/// </summary>
		public override int SwitchActivePlayer(ActiveGame theGame)
		{
			if (theGame.activePlayerId == 0)
			{
				return 1;
			}
			else
			{
				return 0;
			}
		}

		/// <summary>
		/// A User has clicked a cell
		/// * Is he the active Player?
		/// * Is the cell empty?
		/// * What must be done?
		/// </summary>
		public void ClickCell(string activeGameId, string userName, string cellId)
		{
			int intActiveGameId = Convert.ToInt32(activeGameId);
			ActiveGame theGame = rep.GetAllActiveGames().First(a => a.ID == intActiveGameId);
			if(theGame == null || theGame.isFinished == true || theGame.isFull == false)
			{
				return;
			}

			int intUserId = (rep.GetAllUsers().First(u => u.UserName == userName)).UserId;

			if(IsActivePlayer(intUserId, intActiveGameId))
			{
				// We were the active player, let's now switch
				int theActivePlayerId = theGame.activePlayerId;
				theGame.activePlayerId = SwitchActivePlayer(theGame);

				// Now let's retrieve the cell state we want to play with this time around
				int intCellId = Convert.ToInt32(cellId);
				TicTacToeState theState = rep.GetAllTicTacToeStates().First(s => s.cellId == intCellId && s.activeGameId == intActiveGameId);
				if(CellIsEmpty(theState))
				{
					if(theActivePlayerId == 0)
					{
						theState.playerId = 0;
					}
					else
					{
						theState.playerId = 1;
					}

					// CheckForWin determines when the game should end, if 0 is returned it means no conclusion was reached and the game continues
					int win = CheckForWin(theGame.ID);
					if(win == 1) // someone won
					{
						PostGameUpdateWin(theGame, intUserId);
					}
					else if(win == -1) // there was a tie
					{
						PostGameUpdateTie(theGame);
					}

					// Save all the changes we made to the objects retrieved from database and submit the new state to the clients
					rep.Save();
					Clients.Group(activeGameId).cellClicked(intCellId, userName, theActivePlayerId, win);
                    // Inform clients who's turn it is
                    Clients.Group(activeGameId).getNextPlayer(theGame.activePlayerId);
				}
			}
		}

		/// <summary>
		/// Updates the database after a win
		/// </summary>
		public void PostGameUpdateWin(ActiveGame theGame, int winnerId)
		{
			UserProfile theWinner = (from u in rep.GetAllUsers()
									 where u.UserId == winnerId
									 select u).SingleOrDefault();

			UserProfile theLoser = (from u in rep.GetAllUsers()
									join p in rep.GetAllPlayers() on u.UserId equals p.userId
									where p.activeGameId == theGame.ID && u.UserId != theWinner.UserId
									select u).SingleOrDefault();

			if(theWinner.score > theLoser.score)
			{
				theWinner.score += 1;
			}
			else
			{
				theWinner.score += 3;
				theLoser.score += -1;
			}

			theGame.isFinished = true;
		}

		/// <summary>
		/// Updates the database after a tie
		/// </summary>
		public void PostGameUpdateTie(ActiveGame theGame)
		{
			UserProfile user1 = (from u in rep.GetAllUsers()
								 join p in rep.GetAllPlayers() on u.UserId equals p.userId
								 where p.activeGameId == theGame.ID && p.playerId == 0
								 select u).SingleOrDefault();

			UserProfile user2 = (from u in rep.GetAllUsers()
								 join p in rep.GetAllPlayers() on u.UserId equals p.userId
								 where p.activeGameId == theGame.ID && p.playerId == 1
								 select u).SingleOrDefault();
            
			if (user1.score < user2.score)
			{
				user1.score += 1;
			}
			else if (user1.score > user2.score)
			{
				user2.score += 1;
			}

			theGame.isFinished = true;
		}
		#endregion
	}
}